#include "sprite.h"

extern int HRES, VRES;
static char *video_buffer = NULL;


char *read_xpm(char* map[], int *wd, int *ht)
{
	__attribute__((unused)) static char read_xpm_jcard;

	int width, height, colors;
	char sym[256];
	int  col;
	int i, j;
	char *pix, *pixtmp, *tmp, *line;
	char symbol;

	// read width, height, colors
	if (sscanf(map[0],"%d %d %d", &width, &height, &colors) != 3) {
		printf("read_xpm: width, height or colors error\n");
		return NULL;
	}
	if (colors > 256) {
		printf("read_xpm: width, height or colors error\n");
		return NULL;
	}

	*wd = width;
	*ht = height;

	for (i=0; i<256; i++)
	sym[i] = 0;

	// read symbols <-> colors 
	for (i=0; i<colors; i++) {
		if (sscanf(map[i+1], "%c %d", &symbol, &col) != 2) {
			printf("read_xpm: incorrect symbol, color at line %d\n", i+1);
			return NULL;
		}
		if (col > 256) {
			printf("read_xpm: incorrect color at line %d\n", i+1);
			return NULL;
		}
		sym[col] = symbol;
	}

	// allocate pixmap memory
	pix = pixtmp = malloc(width*height);

	// parse each pixmap symbol line
	for (i=0; i<height; i++) {
		line = map[colors+1+i];
		for (j=0; j<width; j++) {
			tmp = memchr(sym, line[j], 256);  // find color of each symbol
			if (tmp == NULL) {
				printf("read_xpm: incorrect symbol at line %d, col %d\n Symbol: %s\n", colors+i+1, j, sym);
				printf("symbol - %c (%d)\n", line[j], line[j]);
				return NULL;
			}
			*pixtmp++ = tmp - sym;
		}
	}

	return pix;
}



char* read_xpm_file(char* filename, int *wd, int *ht)
{
	FILE* f = fopen(filename, "r");
	if(!f) return NULL;

	//FILE* ferror = fopen("error.txt", "w");
	
	int width, height, colors;
	char sym[256];
	int  col;
	int i, j;
	char *pix, *pixtmp, *tmp, *line;
	char symbol;

	// read width, height, colors
	if (fscanf(f,"%d %d %d\n", &width, &height, &colors) != 3) {
		//fprintf(ferror,"read_xpm_file: incorrect width, height, colors\n");
		return NULL;
	}

	if (width > HRES || height > VRES || colors > 256) {
		//fprintf(ferror,"read_xpm_file: incorrect width, height, colors\n");
		return NULL;
	}

	*wd = width;
	*ht = height;

	for (i=0; i<256; i++)
		sym[i] = 0;

	// read symbols <-> colors 
	for (i=0; i<colors; i++) {
		if (fscanf(f, "%c %d\n", &symbol, &col) != 2) {
			//fprintf(ferror,"read_xpm_file: incorrect symbol, color at line %d\n", i+1);
			return NULL;
		}
		if (col > 256) {
			//fprintf(ferror,"read_xpm_file: incorrect color at line %d\n", i+1);
			return NULL;
		}
		sym[col] = symbol;
	}

	// allocate pixmap memory
	pix = pixtmp = malloc(width*height);
	line = malloc(width + 1);

	// parse each pixmap symbol line
	for (i=0; i<height; i++) {
		fscanf(f, "%s\n", line);
		for (j=0; j<width; j++) {
			tmp = memchr(sym, line[j], 256);  // find color of each symbol
			if (tmp == NULL) {
				//fprintf(ferror,"file: %s read_xpm_file: incorrect symbol at line %d, col %d\n Symbol: %c\n",filename ,colors+i+1, j, line[j]);
				return NULL;
			}
			*pixtmp++ = tmp - sym;
		}
	}

	free(line);
	return pix;
}



Sprite* create_sprite(char *pic[])
{
	Sprite* sprt = create_unbuffered_sprite(pic);
	if(!sprt) return NULL;
	sprt->bgmap = malloc(sprt->width * sprt->height);
	return sprt;
}



Sprite* create_unbuffered_sprite(char *pic[])
{
	Sprite* sprt = malloc(sizeof(Sprite));
	if(!sprt) return NULL;
	
	int wd, ht;
	sprt->map = read_xpm(pic, &wd, &ht);
	if(!sprt->map) { free(sprt); return NULL; }
	
	sprt->x = sprt->y = sprt->xspeed = sprt->yspeed = 0;
	sprt->width = wd; sprt->height = ht;
	sprt->bgmap = NULL;
	return sprt;
}



Sprite* create_unbuffered_sprite_from_file(char* filename)
{
	Sprite* sprt = malloc(sizeof(Sprite));
	if(!sprt) return NULL;
	
	int wd, ht;
	sprt->map = read_xpm_file(filename, &wd, &ht);
	if(!sprt->map) { free(sprt); return NULL; }
	
	sprt->x = sprt->y = sprt->xspeed = sprt->yspeed = 0;
	sprt->width = wd; sprt->height = ht;
	sprt->bgmap = NULL;
	return sprt;
}

Sprite* capture_screen(int x_ini, int y_ini, int width, int height, char* base)
{
	Sprite* sprt = malloc(sizeof(Sprite));
	if(!sprt) return NULL;
	
	char* map = malloc(width * height);
	if(!map) { free(sprt); return NULL; }

	int i, k;
	for(i = 0; i < height; i++)
		for(k = 0; k < width; k++)
			*(map + width*i + k) = get_pixel(x_ini + k, y_ini + i, base);

	sprt->map = map;
	
	sprt->x = x_ini;
	sprt->y = y_ini;
	sprt->xspeed = sprt->yspeed = 0;
	sprt->width = width; sprt->height = height;
	sprt->bgmap = NULL;
	return sprt;
}



void draw_sprite(Sprite* sprt, char *base)
{
	char* dSprt = sprt->map;
	char* bgMap = sprt->bgmap;
	
	int i,k;
	for(i=0; i < sprt->height; i++)
		for(k=0; k < sprt->width; k++)
		{
			if(bgMap) *(bgMap + sprt->width*i + k) = get_pixel(sprt->x+k, sprt->y+i, base); //guarda o valor antigo
			if(*dSprt != 36) set_pixel(sprt->x+k,sprt->y+i, *dSprt, base);  //actualiza com o valor actual
			dSprt++;
		}
}



void draw_sprite_at(Sprite* sprt, int x, int y, char *base)
{
	delete_sprite(sprt, base);
	sprt->x = x;
	sprt->y = y;
	draw_sprite(sprt, base);
}


void delete_sprite(Sprite *sprt, char *base)
{
	char* bgMap = sprt->bgmap;
	if(!bgMap) return;

	int i, k;
	for(i=0; i < sprt->height; i++)
		for(k=0; k < sprt->width; k++)
			set_pixel(sprt->x+k,sprt->y+i, *(bgMap + sprt->width*i + k), base);
		
}
/*
void delete_character_sprite(Sprite *sprt, char *base)
{
	char* bgMap = sprt->bgmap;
	if(!bgMap) return;

	int i, k;
	for(i=0; i < sprt->height; i++)
		for(k=0; k < sprt->width; k++)
			set_pixel(sprt->x+k,sprt->y+i, *(bgMap + sprt->width*i + k), base);
		
}
*/


void destroy_sprite(Sprite *sprt)
{
	if (!sprt) return;	
	free(sprt->map);
	free(sprt->bgmap);
	free(sprt);
}

void draw_sprite_scaled(Sprite *sprt, char *base, int width, int height)
{
	char *dSprt = sprt->map;
	char* bgMap = sprt->bgmap;
	if(bgMap)
	{
		bgMap = realloc(bgMap, width * height);
		sprt->bgmap = bgMap;
	}
	
	double dx = (double)sprt->width / width;
	double dy = (double)sprt->height / height;
	
	int i,k;
	for(i = 0; i < height; i++)
		for(k = 0; k < width; k++)
		{
			if(bgMap) *(bgMap + width*i + k) = get_pixel(sprt->x+k, sprt->y+i, base); //guarda o valor antigo
			
			int map_x = dx*k;
			int map_y = dy*i;
			int color = *(dSprt + sprt->width*map_y + map_x);
			if(color != 13) set_pixel(sprt->x+k,sprt->y+i, color, base);  //actualiza com o valor mapeado
		}
}

void delete_sprite_scaled(Sprite *sprt, char *base, int width, int height)
{
	char* bgMap = sprt->bgmap;
	if(!bgMap) return;
	
	int i, k;
	for(i = 0; i < height; i++)
		for(k = 0; k < width; k++)
			set_pixel(sprt->x+k,sprt->y+i, *(bgMap + width*i + k), base);

	bgMap = realloc(bgMap, sprt->width * sprt->height);
	sprt->bgmap = bgMap;		
}

void move_sprite(Sprite *sprt, int x, int y, char *base)
{
	int xi = (x > sprt->x) ? sprt->x : x;
	int yi = (y > sprt->y) ? sprt->y : y;
	int xf = (x > sprt->x) ? x + sprt->width : sprt->x + sprt->width;
	int yf = (y > sprt->y) ? y + sprt->height : sprt->y + sprt->height;
	
	video_buffer = realloc(video_buffer, HRES * VRES);
	flip_buffer_partial(video_buffer, base, xi, yi, xf - xi, yf - yi);
	
	delete_sprite(sprt, video_buffer);
	sprt->x = x;
	sprt->y = y;
	draw_sprite(sprt, video_buffer);
	
	flip_buffer_partial(base, video_buffer, xi, yi, xf - xi, yf - yi);
}



void flip_buffer(char* dest, char* src)
{
	memcpy(dest, src, HRES*VRES);
}



void flip_buffer_partial(char* dest, char* src, int xi, int yi, int width, int height)
{
	if(xi + width >= HRES)
		width = HRES - xi;
	if(xi < 0) {
		width += xi;
		xi = 0;
	}
	if(yi + height >= VRES)
		height = VRES - yi;
	if(yi < 0) {
		height += yi;
		yi = 0;
	}

	int i;
	for (i = 0; i < height; i++)
		memcpy(dest + (yi+i)*HRES + xi, src + (yi+i)*HRES + xi, width);
}
